package com.zhouzifei.tool.fileClient;

import com.zhouzifei.cache.FileCacheEngine;
import com.zhouzifei.tool.common.ServiceException;
import com.zhouzifei.tool.config.FileProperties;
import com.zhouzifei.tool.consts.StorageTypeConst;
import com.zhouzifei.tool.dto.CheckFileResult;
import com.zhouzifei.tool.dto.VirtualFile;
import com.zhouzifei.tool.entity.MetaDataRequest;
import com.zhouzifei.tool.listener.ProgressListener;
import com.zhouzifei.tool.media.file.util.StreamUtil;
import com.zhouzifei.tool.service.ApiClient;
import com.zhouzifei.tool.util.FileUtil;
import com.zhouzifei.tool.util.RandomsUtil;
import com.zhouzifei.tool.util.StringUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.Date;

/**
 * @author 周子斐 (17600004572@163.com)
 * @version 1.0
 * @remark 2019年7月16日
 * @since 1.0
 */
public abstract class BaseApiClient implements ApiClient {

    protected String storageType;
    protected String folder = "";
    public ProgressListener progressListener = newListener();
    protected String suffix;
    protected String newFileName;
    protected String newFileUrl;
    protected final Object LOCK = new Object();
    protected final FileCacheEngine cacheEngine = new FileCacheEngine();
    protected static final String SLASH = "/";
    protected static final String TAG = "PartETag";
    protected static final Integer ONE_INT = 1;

    protected ProgressListener newListener() {
        return new ProgressListener() {
            @Override
            public void start(String msg) {
            }

            @Override
            public void process(int finished, int sum) {
            }

            @Override
            public void end(VirtualFile virtualFile) {
            }
        };
    }

    public BaseApiClient(String storageType) {
        this.storageType = storageType;
    }
    public BaseApiClient(StorageTypeConst storageTypeConst, FileProperties fileProperties) {
        String storageType = storageTypeConst.getStorageType();
        final ApiClient apiClient = getApiClient(storageTypeConst, fileProperties);
        this=  apiClient;
        this.storageType = storageType;
    }

    public BaseApiClient init(FileProperties fileProperties);
    public ApiClient setProgressListener(ProgressListener progressListener) {
        this.progressListener = progressListener;
        return this;
    }

    @Override
    public VirtualFile uploadFile(MultipartFile file) {
        if (file == null) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：文件不可为空");
        }
        try {
            this.newFileName = file.getOriginalFilename();
            return this.uploadFile(file.getInputStream(),this.newFileName);
        } catch (IOException e) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：" + e.getMessage());
        }
    }

    @Override
    public VirtualFile uploadFile(File file) {
        if (file == null) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：文件不可为空");
        }
        this.newFileName = file.getName();
        try (InputStream is = new BufferedInputStream(new FileInputStream(file))) {
            return this.uploadFile(is, file.getName());
        } catch (IOException e) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：" + e.getMessage());
        }
    }

    @Override
    public VirtualFile uploadFile(InputStream is, String fileName) {
        Date startTime = new Date();
        this.newFileName = fileName;
        this.checkName();
        try (InputStream uploadIs = StreamUtil.clone(is);
             InputStream fileHashIs = StreamUtil.clone(is)) {
            final String filePath = this.uploadInputStream(uploadIs, newFileName);
            return VirtualFile.builder().originalFileName(this.newFileName).suffix(this.suffix).uploadStartTime(startTime).uploadEndTime(new Date()).filePath(filePath).fileHash(DigestUtils.md5DigestAsHex(fileHashIs)).fullFilePath(this.newFileUrl).build();
        } catch (IOException ex) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：" + ex.getMessage());
        }
    }

    /**
     * 将网络图片转存到云存储中
     *
     * @param userName 网络图片地址
     * @param referer  为了预防某些网站做了权限验证，不加referer可能会403
     */
    @Override
    public VirtualFile saveToCloudStorage(String userName, String referer, String fileName) {
        try (InputStream is = FileUtil.getInputStreamByUrl(userName, referer)) {
            if (StringUtils.isEmpty(fileName)) {
                fileName = userName;
            }
            return this.uploadFile(is, fileName);
        } catch (Exception e) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：" + e.getMessage());
        }
    }

    protected abstract void check();

    protected  void checkName() {
        this.check();
        if(StringUtils.isEmpty(this.newFileName)||exists(this.newFileName)){
            createNewFileName();
        }
    }

    protected abstract String uploadInputStream(InputStream is, String fileName);

    @Override
    public void downloadFileToLocal(String key, String localFile) {
        this.check();
        InputStream content = this.downloadFileStream(key);
        String saveFile = localFile + key;
        FileUtil.mkdirs(saveFile);
        FileUtil.down(content, saveFile);
    }

    void createNewFileName() {
        this.suffix = FileUtil.getSuffix(this.newFileName);
        this.newFileName = folder + RandomsUtil.alpha(16) + suffix;
    }

    String checkDomainUrl(String domainUrl) {
        return domainUrl.endsWith("/") ? domainUrl : domainUrl + "/";
    }

    @Override
    public VirtualFile multipartUpload(File file, MetaDataRequest metaDataRequest) {
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            return this.multipartUpload(fileInputStream, metaDataRequest);
        } catch (FileNotFoundException e) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：" + e.getMessage());
        }
    }

    @Override
    public VirtualFile multipartUpload(MultipartFile file, MetaDataRequest metaDataRequest) {
        if (file == null) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：文件不可为空");
        }
        try {
            return this.multipartUpload(file.getInputStream(), metaDataRequest);
        } catch (IOException e) {
            throw new ServiceException("[" + this.storageType + "]文件上传失败：" + e.getMessage());
        }
    }

    @Override
    public CheckFileResult checkFile(MetaDataRequest metaDataRequest, HttpServletRequest request) {
        return null;
    }

    @Override
    public VirtualFile resumeUpload(InputStream inputStream, String fileName) {
        return null;
    }
    public ApiClient getApiClient(StorageTypeConst storageTypeConst,FileProperties fileProperties) {
        String storageType = storageTypeConst.getStorageType();
        if(StorageTypeConst.LOCAL.getStorageType().equals(storageType)) {
            final LocalApiClient localApiClient = new LocalApiClient(fileProperties);
            return localApiClient.setProgressListener(progressListener);
        }else if(StorageTypeConst.QINIUYUN.getStorageType().equals(storageType)){
            return new QiniuApiClient(fileProperties);
        }else if(StorageTypeConst.ALIYUN.getStorageType().equals(storageType)){
            String aliEndpoint = fileProperties.getAliEndpoint();
            String aliAccessKey = fileProperties.getAliAccessKey();
            String aliSecretKey = fileProperties.getAliSecretKey();
            String aliUrl = fileProperties.getAliUrl();
            String aliBucketName = fileProperties.getAliBucketName();
            if (!fileProperties.getAliOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new AliyunOssApiClient().init(aliEndpoint, aliAccessKey, aliSecretKey, aliUrl, aliBucketName);
        }else if(StorageTypeConst.YOUPAIYUN.getStorageType().equals(storageType)) {
            String uPaiUserName = fileProperties.getUPaiUserName();
            String uPaiPassWord = fileProperties.getUPaiPassWord();
            String uPaiUrl = fileProperties.getUPaiUrl();
            String uPaiBucketName = fileProperties.getUPaiBucketName();
            if (!fileProperties.getUPaiOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new UpaiyunOssApiClient().init(uPaiUserName, uPaiPassWord,uPaiBucketName,uPaiUrl);
        }else if(StorageTypeConst.TENGXUNYUN.getStorageType().equals(storageType)) {
            String qCloudAccessKey = fileProperties.getQCloudAccessKey();
            String qCloudSecretKey = fileProperties.getQCloudSecretKey();
            String qCloudEndpoint = fileProperties.getQCloudEndpoint();
            String qCloudUrl = fileProperties.getQCloudUrl();
            String qCloudBucketName = fileProperties.getQCloudBucketName();
            if (!fileProperties.getQCloudOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new QCloudOssApiClient().init(qCloudAccessKey, qCloudSecretKey,qCloudEndpoint,qCloudBucketName,qCloudUrl);
        }else if(StorageTypeConst.HUAWEIYUN.getStorageType().equals(storageType)) {
            String huaweiAccessKey = fileProperties.getHuaweiAccessKey();
            String huaweiSecretKey = fileProperties.getHuaweiSecretKey();
            String huaweiEndpoint = fileProperties.getHuaweiEndpoint();
            String huaweiUrl = fileProperties.getHuaweiUrl();
            String huaweiBucketName = fileProperties.getHuaweiBucketName();
            if (!fileProperties.getHuaweiOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new HuaweiCloudOssApiClient().init(huaweiAccessKey, huaweiSecretKey,huaweiEndpoint,huaweiBucketName,huaweiUrl);
        }else if(StorageTypeConst.FASTDFS.getStorageType().equals(storageType)) {
            String fastDFSServerUrl = fileProperties.getFastDFSServerUrl();
            String fastDFSUrl = fileProperties.getFastDFSUrl();
            if (!fileProperties.getFastDFSOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new FastDfsOssApiClient().init(fastDFSServerUrl, fastDFSUrl);
        }else if(StorageTypeConst.SMMS.getStorageType().equals(storageType)) {
            String smmsUserName = fileProperties.getSmmsUserName();
            String smmsPassWord = fileProperties.getSmmsPassWord();
            String smmsToken = fileProperties.getSmmsToken();
            if (!fileProperties.getSmmsOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new SmMsApiClient().init(smmsUserName, smmsPassWord, smmsToken);
        }else if(StorageTypeConst.XMLY.getStorageType().equals(storageType)) {
            String xmlyCookie = fileProperties.getXmlyCookie();
            if (!fileProperties.getXmlyOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new XMLYApiClient().init(xmlyCookie);
        }else if(StorageTypeConst.GITHUB.getStorageType().equals(storageType)) {
            String githubRepository = fileProperties.getGithubRepository();
            String githubToken = fileProperties.getGithubToken();
            String githubUser = fileProperties.getGithubUser();
            if (!fileProperties.getXmlyOpen()) {
                throw new ServiceException("[" + storageType + "]尚未开启，文件功能暂时不可用！");
            }
            return new GithubApiClient().init(githubToken,githubUser,githubRepository);
        }else{
            throw new ServiceException("[文件服务]请选择文件存储类型！");
        }
    }
}
